//################################################################################
// MIT License
// Copyright (c) 2024 ZhangYihua
//
// Change Logs:
// Date           Author       Notes
// 2020-12-20     ZhangYihua   first version
//
// Description  : 
//################################################################################

module nco #(
parameter           PHS_FW                  = 11,   // phase fraction width
parameter           NCO_FW                  = 16,   // ACC fraction width, NCO_FW>=PHS_FW
parameter           FREQ_DW                 = 16,   // FREQ_DW<=NCO_FW or FREQ_DW>NCO_FW is both OK
parameter           RND_EN                  = 1'b0, // 1'b0:truncate lower bits directly; 1'b1:rounding, but over_1p is not used in this mode

// the following parameters are calculated automatically
parameter           OVER_BW                 = (FREQ_DW<=NCO_FW) ? 1 : 1+FREQ_DW-NCO_FW
) ( 
input                                       rst_n,
input                                       clk,
input                                       cke,

input               [FREQ_DW-1:0]           freq_word,  // u(FREQ_DW, NCO_FW), output frequency is F_clk*freq_word/(2^NCO_FW)

output              [OVER_BW-1:0]           over_1p,    // combinational logic output, 1 clk previous than pahse
output              [PHS_FW-1:0]            phase       // u(PHS_FW, PHS_FW), only fraction bits
);

//################################################################################
// define local varialbe and localparam
//################################################################################
wire                [NCO_FW+OVER_BW-1:0]    cnt_acc_c;
reg                 [NCO_FW-1:0]            cnt_acc;

//################################################################################
// main
//################################################################################

assign cnt_acc_c = $unsigned(cnt_acc) + $unsigned(freq_word);
always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        cnt_acc <=`U_DLY {NCO_FW{1'b0}};
    end else if (cke==1'b1) begin
        cnt_acc <=`U_DLY cnt_acc_c[0+:NCO_FW];
    end else
        ;
end
assign over_1p = cnt_acc_c[NCO_FW+:OVER_BW];

generate if (RND_EN==1'b1 && NCO_FW>PHS_FW) begin:G_RND

    assign phase = cnt_acc[NCO_FW-1-:PHS_FW] + $unsigned(cnt_acc[NCO_FW-1-PHS_FW]);

end else begin:G_NRND
    
    assign phase = cnt_acc[NCO_FW-1-:PHS_FW];

end endgenerate

//################################################################################
// ASSERTION
//################################################################################

`ifdef CBB_ASSERT_ON
// synopsys translate_off

initial begin
    if (NCO_FW<PHS_FW) begin
        $error("NCO_FW is smaller than PHS_FW.");
        $stop;
    end
end

// synopsys translate_on
`endif

endmodule
